package concurrency.lock;

import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

/**
 * 基于显式的链表实现的公平锁
 * @Author: hao.zhang
 * @Date: 9/6/19 2:46 PM
 */
public class SimpleMCSLock {

    volatile MCSNode tail;

    private static final AtomicReferenceFieldUpdater<SimpleMCSLock, MCSNode> UPDATER = AtomicReferenceFieldUpdater
        .newUpdater(SimpleMCSLock.class, MCSNode.class, "tail");

    public void lock(MCSNode currentThread) {
        // step 1. 找出最近一个申请者. 同时也是链表的尾部。
        MCSNode predecessor = UPDATER.getAndSet(this, currentThread);
        if (predecessor != null) {
            // step 2. 如果最近申请者不为空，则将当前申请者设为前者的后继节点
            predecessor.next = currentThread;
            // step 3. 阻塞 通过前驱节点的释放锁操作来改动isBlock属性值，以此来确定该线程是否可以获取锁。 这里是自旋本地变量，等待前驱节点改变这个变量的值
            while (currentThread.isBlock) {}
        } else {
            // 只有一个(第一个进入的)线程使用锁，没有前驱节点来通知它，所以需要标记自己为非阻塞
            currentThread.isBlock = false;
        }
        // 走到这里，说明获取到了锁
    }



    public void unlock(MCSNode currentThread) {
        if (currentThread.isBlock) {
            // 该线程并当前未获取到锁
            return;
        }
        if (currentThread.next == null) {
            // step 4. 二次确认是否有后继节点。
            if (UPDATER.compareAndSet(this, currentThread, null)) {
                // compareAndSet 返回true表示确实没有后继节点。 返回true表明当前持有锁的是currentThread，还没有后续线程尝试获取锁。因为有线程尝试获取锁时，UPDATER会被更新
                return;
            } else {
                // 二次确认时，突然有了后继节点
                // 这里忙等，是因为 step1执行完之后，step2可能还没执行
                while (currentThread.next == null) {}
            }
        }
        // 走到这里说明有后继节点，需要通知后继节点可以获取锁
        currentThread.next.isBlock = false;
        // currentThread can be GC.
        currentThread.next = null;
    }


    static class MCSNode {
        volatile MCSNode next;
        volatile boolean isBlock = true;
    }
}
